home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / rclesrc.10 / ircle sources / DCC.p < prev    next >
Encoding:
Text File  |  1992-09-06  |  16.0 KB  |  721 lines

  1. {    ircle - Internet Relay Chat client    }
  2. {    File: DCC    }
  3. {    Copyright ⌐ 1992 Olaf Titz (s_titz@iravcl.ira.uka.de)    }
  4.  
  5. {    This program is free software; you can redistribute it and/or modify    }
  6. {    it under the terms of the GNU General Public License as published by    }
  7. {    the Free Software Foundation; either version 2 of the License, or    }
  8. {    (at your option) any later version.    }
  9.  
  10. {    This program is distributed in the hope that it will be useful,    }
  11. {    but WITHOUT ANY WARRANTY; without even the implied warranty of    }
  12. {    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    }
  13. {    GNU General Public License for more details.    }
  14.  
  15. {    You should have received a copy of the GNU General Public License    }
  16. {    along with this program; if not, write to the Free Software    }
  17. {    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.    }
  18.  
  19. unit DCC;
  20. { Handles DCC connections }
  21.  
  22. interface
  23. uses
  24.     TCPTypes, TCPStuff, TCPConnections, ApplBase, MsgWindows, {}
  25.     IRCGlobals, IRCaux, IRCChannels;
  26.  
  27. procedure InitDCC;
  28. { startup }
  29.  
  30. procedure DCCRequest (var fr, s: string);
  31. { Request for DCC from outside }
  32.  
  33. procedure DCCcommand (var s: string);
  34. { user DCC command }
  35.  
  36. procedure DCCChatSend (var s: string);
  37. { send line over DCC CHAT }
  38.  
  39. implementation
  40. { Largely an adaption of the IRCII DCC code by Troy Rollo }
  41.  
  42. {$SETC DCCenable=true}
  43. {$SETC DEBUG=false}
  44.  
  45. const
  46.     BUFSIZ = 4096;
  47.  
  48. {$IFC DCCenable}
  49. type
  50.     DCCType = (CHAT, SEND, GET);
  51.     DCCFlagsType = (CLOSED, WAITING, OFFERED, OPENING, OPEN);
  52.     DCCPtr = ^DCCrec;
  53.     DCCrec = record
  54.             next: DCCPtr;            { Link }
  55.             flag: DCCFlagsType;    { status }
  56.             peer: string[16];        { peer (nick) }
  57.             host, port: longint;        { TCP host/port }
  58.             sock: ConnectionIndex;    { socket }
  59.             case typ : DCCType of
  60.                 CHAT: (
  61.                         win: MWHndl;    { window for DCC CHAT messages }
  62.                 );
  63.                 SEND: (
  64.                         sfil: SFReply;    { contains file parameters }
  65.                         sfref: integer;    { file refnum }
  66.                         stext: boolean;    { textfile conversion }
  67.                         sendbuf: CharsPtr;{ Buffer pointer }
  68.                         sent: longint;    { Bytes already sent }
  69.                         sendmax: longint;    { Bytes to send }
  70.                 );
  71.                 GET: (
  72.                         gfil: SFReply;    { contains file parameters }
  73.                         gfref: integer;    { file refnum }
  74.                         gtext: boolean;    { textfile conversion }
  75.                         getbuf: CharsPtr;    { Buffer pointer }
  76.                         gotten: longint;    { Bytes received }
  77.                 );
  78.         end;
  79.     CEPtr = ^connectionEventRecord;
  80.  
  81. var
  82.     FDCC: DCCPtr;
  83.  
  84. { The following is really not beautiful, but DCC protocol requires IP addresses }
  85. { to be given as unsigned long in ASCII, while this compiler has only signed longint. Sigh. }
  86. {$V-}
  87. function ulongval (var s: string): longint;
  88.     var
  89.         l: longint;
  90.         i, j: integer;
  91.     begin
  92.         l := 0;
  93.         i := 1;
  94.         repeat
  95.             if i > length(s) then
  96.                 leave;
  97.             j := ord(s[i]) - 48;
  98.             if (j < 0) or (j > 9) then
  99.                 leave;
  100.             l := 10 * l + j;
  101.             i := succ(i);
  102.         until false;
  103.         ulongval := l;
  104.     end;
  105.  
  106. procedure str10neg (var s: string);
  107. { Given s as a 10-digit ASCII number, this will compute 2^32-s, i.e. the }
  108. { 32-bit twos complement of s. }
  109.     var
  110.         y: string[10];
  111.         i, x, c: integer;
  112.     begin
  113.         y := '4294967296'; { 2^32 }
  114.         c := 0;
  115.         for i := 10 downto 1 do begin
  116.             x := ord(y[i]) - ord(s[i]) - c;
  117.             if x < 0 then begin
  118.                 c := 1;
  119.                 x := x + 10
  120.             end
  121.             else
  122.                 c := 0;
  123.             s[i] := chr(x + 48)
  124.         end
  125.     end;
  126.  
  127. procedure ulongstr (l: longint; var s: string);
  128.     var
  129.         i: integer;
  130.         n: boolean;
  131.     begin
  132.         if l = 0 then
  133.             debugstr('Bogus IP/port number 0'); { here it is guaranteed to be nonzero! }
  134.         n := (l < 0);
  135.         l := abs(l);
  136.         for i := 10 downto 1 do begin
  137.             s[i] := chr((l mod 10) + 48);
  138.             l := l div 10
  139.         end;
  140.         s[0] := chr(10);
  141.         if n then
  142.             str10neg(s);
  143.         while s[1] = '0' do
  144.             delete(s, 1, 1);
  145.     end;
  146.  
  147.  
  148. { U*X open/create substitutes }
  149.  
  150. function openoldfile (x: DCCPtr): OSErr;
  151.     begin
  152.         openoldfile := FSOpen(x^.sfil.fName, x^.sfil.vRefNum, x^.sfref)
  153.     end;
  154.  
  155. function opennewfile (x: DCCPtr): OSErr;
  156.     var
  157.         e: OSErr;
  158.         b1, b2: OSType;
  159.     begin
  160.         if x^.gtext then begin
  161.             b1 := 'EDIT';
  162.             b2 := 'TEXT';
  163.         end
  164.         else begin
  165.             b1[0] := chr(0);
  166.             b1[1] := chr(0);
  167.             b1[2] := chr(0);
  168.             b1[3] := chr(0);
  169.             b2 := b1
  170.         end;
  171.         e := create(x^.gfil.fName, x^.gfil.vRefNum, b1, b2);
  172.         if (e = noErr) or (e = dupFNErr) then
  173.             e := openoldfile(x); { XX: assumes sfil==gfil }
  174.         opennewfile := e
  175.     end;
  176.  
  177. function getDCC (ty: DCCType; var from: string; var f: DCCPtr): boolean;
  178. { will return true if dcc already existed, allocate it otherwise }
  179.     begin
  180.         getDCC := true;
  181.         f := FDCC;
  182.         while f <> nil do
  183.             with f^ do begin
  184.                 if (typ = ty) and (peer = from) then
  185.                     exit(getDCC);
  186.                 f := f^.next;
  187.             end;
  188.         new(f);
  189.         if f = nil then
  190.             exit(getDCC); { error condition, will lead to collision msg }
  191.         with f^ do begin
  192.             next := FDCC;
  193.             typ := ty;
  194.             flag := CLOSED;
  195.             peer := from;
  196.             host := 0;
  197.             port := 0;
  198.             sock := 0;
  199.             win := nil;
  200.             sfref := 0;
  201.             if (ty = SEND) or (ty = GET) then { XX: assume sendbuf==getbuf }
  202.                 begin
  203.                 sendbuf := CharsPtr(NewPtr(BUFSIZ));
  204.                 if MemError <> 0 then
  205.                     exit(getDCC);
  206.             end;
  207.         end;
  208.         FDCC := f;
  209.         getDCC := false;
  210.     end;
  211.  
  212. procedure killDCC (x: DCCPtr);
  213.     var
  214.         p: DCCPtr;
  215.         t: string[12];
  216.         i: integer;
  217.     begin
  218.         if x^.sock <> 0 then    { Forcibly close connection }
  219.             AbortConnection(x^.sock);
  220.         if x^.win <> nil then begin    { close chat window }
  221.             t := concat(DCC_CHAT_PREFIX, x^.peer, DCC_CHAT_PREFIX);
  222.             DoPart(t);
  223.             x^.win := nil
  224.         end;
  225.         if x^.sfref <> 0 then    { close file }
  226.             i := FSClose(x^.sfref);
  227.         if (x^.typ = SEND) or (x^.typ = GET) then begin
  228.             DisposPtr(Ptr(x^.sendbuf));
  229.             if x^.flag = OPEN then begin    { sync FT counter }
  230.                 NFT := pred(NFT);
  231.                 UpdateStatusLine
  232.             end
  233.         end;
  234.         if x = FDCC then
  235.             FDCC := x^.next
  236.         else begin
  237.             p := FDCC;
  238.             while p^.next <> x do begin
  239.                 if p^.next = nil then
  240.                     debugstr('Error in DCC chain');
  241.                 p := p^.next
  242.             end;
  243.             p^.next := x^.next;
  244.         end;
  245.         dispose(x);
  246.     end;
  247.  
  248. { Give message for failed DCC and close }
  249. procedure failedDCC (x: DCCPtr; e: string);
  250.     var
  251.         s: string[120];
  252.     begin
  253.         s := stringof('*** ', e, ' DCC ', x^.typ, ' connection to ', x^.peer);
  254.         Message(s);
  255.         killDCC(x)
  256.     end;
  257.  
  258. { This gets called on user closing DCC CHAT window }
  259. procedure closeDChat (w: WindowPtr);
  260.     var
  261.         x: DCCPtr;
  262.         t: str255;
  263.     begin
  264.         GetWTitle(w, t);
  265.         delete(t, 1, 1);
  266.         t[0] := pred(t[0]);
  267.         if not getDCC(CHAT, t, x) then
  268.             debugstr(concat('Bogus DCC CHAT window ', t));
  269.         CloseConnection(x^.sock);
  270.         KillDCC(x);
  271.     end;
  272.  
  273. { Send a portion of BUFSIZ bytes over DCC. }
  274. { XX: DCC SEND protocol assumes that receiver sends acknowledge as }
  275. { four byte high-endian integer. Mac is high-endian so no conversion is needed. }
  276. procedure DCCsendfile (p: DCCPtr; c: TCPConnectionPtr);
  277.     var
  278.         nn: longint;
  279.         i, e: integer;
  280. {$IFC DEBUG}
  281.         s: string[80];
  282. {$ENDC}
  283.     begin
  284.         nn := p^.sendmax;
  285.         if nn = 0 then begin
  286.             CloseConnection(p^.sock);
  287.             failedDCC(p, 'Closed');
  288.             exit(DCCSendfile)
  289.         end;
  290.         if nn > BUFSIZ then
  291.             nn := BUFSIZ;
  292.         if FSRead(p^.sfref, nn, Ptr(p^.sendbuf)) = 0 then begin
  293.             if p^.stext then
  294.                 for i := 0 to nn - 1 do
  295.                     p^.sendbuf^[i] := ISOEncode^^[p^.sendbuf^[i]];
  296.             p^.sendmax := p^.sendmax - nn;
  297.             p^.sent := p^.sent + nn;
  298. {$IFC DEBUG}
  299.             s := stringof('* Sending ', nn : 1, ' bytes');
  300.             Message(s);
  301. {$ENDC}
  302.             e := 0;
  303.             i := TCPSendAsync(c, Ptr(p^.sendbuf), nn, @e);
  304.             if i = 0 then begin
  305.                 repeat
  306.                     ApplRun
  307.                 until i <> inProgress;
  308.             end;
  309.             if (i <> 0) or (e <> 0) then
  310.                 failedDCC(p, 'Lost (send error)');
  311.         end
  312.         else
  313.             failedDCC(p, 'Lost (file read error)');
  314.     end;
  315.  
  316.  
  317. procedure DCCgotline (p: DCCPtr; c: CEPtr);
  318.     var
  319.         s: string;
  320.         nn: longint;
  321.         b: boolean;
  322.         i, j: integer;
  323.     begin
  324.         case p^.typ of
  325.             CHAT: 
  326.                 begin
  327.                 nn := 1;
  328.                 if TCPReceiveUpTo(c^.tcpc, 10, readTimeout, @s[0], 250, nn, b) = 0 then begin
  329.                     j := nn - 1;
  330.                     while (j > 0) and (s[j] in [chr(10), chr(13)]) do
  331.                         j := pred(j);
  332.                     s[0] := chr(j);
  333.                     for i := 1 to j do
  334.                         s[i] := ISODecode^^[s[i]];
  335.                     MWMessage(p^.win, s);
  336.                 end
  337.             end;
  338.             SEND: 
  339.                 begin
  340.                 if TCPReceiveChars(c^.tcpc, @nn, sizeof(nn)) = 0 then begin
  341.  {$IFC DEBUG}
  342.                     s := stringof('* Acknowledged ', nn : 1, ' bytes');
  343.                     Message(s);
  344. {$ENDC}
  345.                     if nn = p^.sent then
  346.                         DCCsendfile(p, c^.tcpc)
  347.                     else if nn > p^.sent then
  348.                         failedDCC(p, 'Lost (bogus acknowledge)')
  349.                 end
  350.                 else
  351.                     failedDCC(p, 'Lost (no acknowledge)');
  352.             end;
  353.             GET: 
  354.                 begin
  355.                 nn := TCPCharsAvailable(c^.tcpc);
  356.                 if nn > BUFSIZ then
  357.                     nn := BUFSIZ;
  358.                 if TCPReceiveChars(c^.tcpc, Ptr(p^.getbuf), nn) = 0 then begin
  359.                     if p^.gtext then begin
  360.                         for i := 0 to nn - 1 do
  361.                             p^.getbuf^[i] := ISODecode^^[p^.getbuf^[i]];
  362.                     end;
  363.                     if FSWrite(p^.gfref, nn, Ptr(p^.getbuf)) = 0 then begin
  364.                         p^.gotten := p^.gotten + nn;
  365. {$IFC DEBUG}
  366.                         s := stringof('* Received ', p^.gotten : 1, ' bytes');
  367.                         Message(s);
  368. {$ENDC}
  369.                         if TCPSend(c^.tcpc, @p^.gotten, sizeof(longint)) <> 0 then
  370.                             failedDCC(p, 'Lost');
  371.                     end
  372.                     else
  373.                         failedDCC(p, 'Lost (file write error)');
  374.                 end
  375.                 else
  376.                     failedDCC(p, 'Lost (receive error)');
  377.             end;
  378.         end;
  379.     end;
  380.  
  381. procedure DCCConnOpened (p: DCCPtr; c: CEPtr);
  382.     var
  383.         t: string[16];
  384.     begin
  385.         p^.flag := OPEN;
  386.         case p^.typ of
  387.             CHAT: 
  388.                 begin
  389.                 t := concat(DCC_CHAT_PREFIX, p^.peer, DCC_CHAT_PREFIX);
  390.                 p^.win := DoJoin(t);
  391.                 if p^.win <> nil then
  392.                     p^.win^^.whenDone := @closeDChat; { XX }
  393.             end;
  394.             SEND: 
  395.                 begin
  396.                 NFT := succ(NFT);
  397.                 UpdateStatusLine;
  398.                 if openoldfile(p) = 0 then begin
  399.                     if getEOF(p^.sfref, p^.sendmax) = 0 then begin
  400.                         p^.sent := 0;
  401.                         DCCsendfile(p, c^.tcpc);
  402.                         exit(DCCConnOpened)
  403.                     end;
  404.                 end;
  405.                 killDCC(p);
  406.             end;
  407.             GET: 
  408.                 begin
  409.                 if opennewfile(p) = 0 then begin
  410.                     NFT := succ(NFT);
  411.                     UpdateStatusLine;
  412.                     p^.gotten := 0;
  413.                 end
  414.                 else
  415.                     killDCC(p)
  416.             end;
  417.         end;
  418.     end;
  419.  
  420.  
  421. function netEvent (var e: EventRecord): boolean;
  422.     var
  423.         p: DCCPtr;
  424.         c: CEPtr;
  425.     begin
  426.         c := CEPtr(e.message);
  427.         p := FDCC;
  428.         while p <> nil do begin
  429.             if p^.sock = c^.connection then begin
  430.                 case c^.event of
  431.                     C_Established: 
  432.                         DCCConnOpened(p, c);
  433.                     C_FailedToOpen: 
  434.                         failedDCC(p, 'Failed to open');
  435.                     C_Closing: 
  436.                         failedDCC(p, 'Closing');
  437.                     C_Closed: 
  438.                         failedDCC(p, 'Closed');
  439.                     C_CharsAvailable: 
  440.                         DCCgotline(p, c)
  441.                 end;
  442.                 netEvent := true;
  443.                 exit(netEvent)
  444.             end;
  445.             p := p^.next;
  446.         end;
  447.         netEvent := false;
  448.     end;
  449.  
  450. procedure openDCC (x: DCCPtr);
  451.     var
  452.         t: TCPConnectionPtr;
  453.         ipa, pn: string[12];
  454.         des: string[64];
  455.         s: string[150];
  456.         i: integer;
  457.     begin
  458.         if x^.flag = CLOSED then begin
  459.             if TCPGetMyIPAddr(x^.host) = 0 then
  460.                 if NewPassiveConnection(x^.sock, 8192, 0, 0, 0, nil) = 0 then begin
  461.                     GetConnectionTCPC(x^.sock, t);
  462.                     x^.port := TCPLocalPort(t);
  463.                     ulongstr(x^.host, ipa);
  464.                     if x^.port < 0 then
  465.                         x^.port := x^.port + 65536;
  466.                     ulongstr(x^.port, pn);
  467.                     if x^.typ = CHAT then
  468.                         des := 'chat'
  469.                     else
  470.                         des := x^.sfil.fName;
  471.                     repeat
  472.                         i := pos(' ', des);
  473.                         if i = 0 then
  474.                             leave;
  475.                         des[i] := '_'
  476.                     until false;
  477.                     s := stringof('PRIVMSG ', x^.peer, ' ', chr(1), 'DCC ', x^.typ, ' ', des, ' ', ipa, ' ', pn, chr(1));
  478.                     PutLine(s);
  479.                     s := stringof('Requesting DCC ', x^.typ, ' connection with ', x^.peer);
  480.                     Message(s);
  481.                     x^.flag := WAITING;
  482.                     exit(openDCC)
  483.                 end;
  484.             failedDCC(x, 'Failed to create');
  485.         end
  486.         else if x^.flag = OFFERED then begin
  487.             if NewActiveConnection(x^.sock, 8192, x^.host, x^.port, nil) = 0 then
  488.                 x^.flag := OPENING
  489.             else
  490.                 failedDCC(x, 'Failed to open');
  491.         end;
  492.     end;
  493.  
  494. {$ENDC}
  495.  
  496. { Process incoming DCC request }
  497. procedure DCCRequest (var fr, s: string);
  498.     var
  499.         c: str255;
  500. {$IFC DCCenable}
  501.         x: DCCPtr;
  502.         t: DCCType;
  503.         i: integer;
  504.         des: string[64];
  505. {$ENDC}
  506.     begin
  507.         NextArg(s, c);
  508.         uprString(c, false);
  509. {$IFC DCCenable}
  510.         if c = 'CHAT' then
  511.             t := CHAT
  512.         else if c = 'SEND' then
  513.             t := SEND
  514.         else begin
  515. {$ENDC}
  516.             c := concat('NOTICE ', fr, ' ', chr(1), 'ERRMSG DCC ', c, ' :Unsupported request', chr(1));
  517.             if serverStatus = 0 then
  518.                 PutLine(c);
  519.             exit(DCCRequest);
  520. {$IFC DCCenable}
  521.         end;
  522.         if getDCC(t, fr, x) then begin
  523.             c := stringof('*** DCC collision for ', t, ' request from ', fr);
  524.             Message(c);
  525.             killDCC(x)
  526.         end
  527.         else begin { request from peer }
  528.             NextArg(s, des);
  529.             repeat
  530.                 i := pos('/', des);
  531.                 if i = 0 then
  532.                     leave;
  533.                 delete(des, 1, i)
  534.             until false;
  535.             repeat
  536.                 i := pos(':', des);
  537.                 if i = 0 then
  538.                     leave;
  539.                 des[i] := '_'
  540.             until false;
  541.             NextArg(s, c);
  542.             x^.host := ulongval(c);
  543.             StringtoNum(s, x^.port);
  544.             x^.flag := OFFERED;
  545.             if x^.typ = SEND then begin
  546.                 x^.typ := GET;
  547.                 x^.gfil.fName := des;
  548.             end;
  549.             c := stringof('*** DCC ', t, ' request from ', fr, ' (', des, ')');
  550.             Message(c);
  551.         end
  552. {$ENDC}
  553.     end;
  554.  
  555. procedure DCCcommand (var s: string);
  556.     label
  557.         39;
  558.     var
  559.         c: str255;
  560. {$IFC DCCenable}
  561.         des: string[64];
  562.         x: DCCPtr;
  563.         t: DCCType;
  564.         b: boolean;
  565.         p: Point;
  566.         ty: SFTypeList;
  567. {$ENDC}
  568.     begin
  569.         NextArg(s, c);
  570.         UprString(c, false);
  571. {$IFC DCCenable}
  572.         if IsChannel(s) then
  573.             c := '*** Target of DCC must be client'
  574.         else if c = 'CHAT' then
  575.             if s = '' then
  576.                 c := '*** Nickname needed for DCC CHAT'
  577.             else begin
  578.                 b := getDCC(CHAT, s, x);
  579.                 c := '';
  580.                 if (x^.flag = WAITING) or (x^.flag = OPENING) or (x^.flag = OPEN) then
  581.                     c := concat('*** DCC CHAT connection with ', s, ' exists already')
  582.                 else
  583.                     openDCC(x);
  584.             end
  585.         else if c = 'CLOSE' then begin
  586.             NextArg(s, c);
  587.             UprString(c, false);
  588.             if c = 'CHAT' then
  589.                 t := CHAT
  590.             else if c = 'SEND' then
  591.                 t := SEND
  592.             else if c = 'GET' then
  593.                 t := GET
  594.             else
  595.                 goto 39;
  596.             NextArg(s, c);
  597.             if c <> '' then begin
  598.                 if not getDCC(t, c, x) then
  599.                     c := stringof('*** No DCC ', t, ' connection to ', c)
  600.                 else
  601.                     c := stringof('*** DCC ', t, ' to ', c, ' closed');
  602.                 killDCC(x);
  603.             end
  604.             else
  605. 39: { Oh yes I know this is spaghetti code ;-) }
  606.                 c := '*** You must supply type and nick for DCC CLOSE';
  607.         end
  608.         else if (c = '') or (c = 'LIST') then begin
  609.             x := FDCC;
  610.             while x <> nil do begin
  611.                 if x^.typ = CHAT then
  612.                     des := 'chat'
  613.                 else
  614.                     des := x^.sfil.fName;
  615.                 c := stringof(x^.typ : 5, x^.peer : 11, x^.flag : 10, ' ', des);
  616.                 Message(c);
  617.                 x := x^.next
  618.             end;
  619.             c := ''
  620.         end
  621.         else if (c = 'TSEND') or (c = 'SEND') then begin
  622.             if s = '' then
  623.                 c := '*** You must supply a nick for DCC SEND'
  624.             else begin
  625.                 b := getDCC(SEND, s, x);
  626.                 if (x^.flag = WAITING) or (x^.flag = OPENING) or (x^.flag = OPEN) then
  627.                     c := concat('*** DCC SEND connection with ', s, ' exists already')
  628.                 else begin
  629.                     x^.stext := (c[1] = 'T');
  630.                     SetPt(p, 80, 30);
  631.                     ty[0] := 'TEXT';
  632.                     c := '';
  633.                     if x^.stext then
  634.                         SFGetFile(p, '', nil, 1, ty, nil, x^.sfil)
  635.                     else
  636.                         SFGetFile(p, '', nil, -1, ty, nil, x^.sfil);
  637.                     if x^.sfil.good then
  638.                         openDCC(x)
  639.                     else
  640.                         killDCC(x);
  641.                 end
  642.             end
  643.         end
  644.         else if (c = 'TGET') or (c = 'GET') then begin
  645.             if s = '' then
  646.                 c := '*** You must supply a nick for DCC GET'
  647.             else begin
  648.                 b := getDCC(GET, s, x);
  649.                 if x^.flag = OFFERED then begin
  650.                     x^.gtext := (c[1] = 'T');
  651.                     c := '';
  652.                     SetPt(p, 80, 30);
  653.                     if x^.gtext then
  654.                         SFPutFile(p, 'Save TEXT file as:', x^.gfil.fName, nil, x^.gfil)
  655.                     else
  656.                         SFPutFile(p, 'Save file as:', x^.gfil.fName, nil, x^.gfil);
  657.                     if x^.gfil.good then
  658.                         openDCC(x);
  659.                 end
  660.                 else begin
  661.                     c := concat('*** No DCC SEND offered from ', s);
  662.                     killDCC(x)
  663.                 end
  664.             end
  665.         end
  666.         else
  667.             c := concat('*** Unknown DCC command: ', c);
  668. {$ELSEC}
  669.         c := '*** DCC not implemented';
  670. {$ENDC}
  671.         if c[0] <> chr(0) then
  672.             Message(c);
  673.     end;
  674.  
  675.  
  676. procedure DCCChatSend (var s: string);
  677. {$IFC DCCEnable}
  678.     var
  679.         i, n, oe: integer;
  680.         p: TCPConnectionPtr;
  681.         x: DCCPtr;
  682.         t: string[12];
  683. {$ENDC}
  684.     begin
  685. {$IFC DCCEnable}
  686.         t := CurrentTarget;
  687.         delete(t, 1, 1);
  688.         t[0] := pred(t[0]);
  689.         if not getDCC(CHAT, t, x) then
  690.             debugstr(concat('Bogus DCC CHAT target ', CurrentTarget));
  691.         insert('> ', s, 1);
  692.         ChannelMsg(CurrentTarget, s);
  693.         n := length(s);
  694.         for i := 1 to n do
  695.             s[i] := ISOEncode^^[s[i]];
  696.         s[n + 1] := chr(10);
  697.         GetConnectionTCPC(x^.sock, p);
  698.         i := TCPSendAsync(p, @s[3], n - 1, @oe);
  699.         if i <> 0 then
  700.             killDCC(x)
  701.         else begin
  702.             repeat
  703.                 ApplRun
  704.             until oe <> inProgress;
  705.             if oe <> 0 then
  706.                 killDCC(x);
  707.         end;
  708. {$ENDC}
  709.     end;
  710.  
  711. procedure InitDCC;
  712.     var
  713.         i: integer;
  714.     begin
  715. {$IFC DCCenable}
  716.         FDCC := nil;
  717.         i := ApplTask(@netEvent, TCPMsg);
  718. {$ENDC}
  719.     end;
  720.  
  721. end.